package org.idansof.otp.ui;

import org.idansof.otp.MetadataSQLOpenHelper;
import org.idansof.otp.OTPProvider;
import org.idansof.otp.client.*;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.xmlpull.v1.XmlPullParserException;

import com.j256.ormlite.android.AndroidConnectionSource;
import com.j256.ormlite.dao.BaseDaoImpl;
import com.j256.ormlite.dao.CloseableIterable;
import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.misc.BaseDaoEnabled;
import com.j256.ormlite.support.ConnectionSource;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.BaseExpandableListAdapter;
import android.widget.Button;
import android.widget.DigitalClock;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

public class FindPlanActivity extends Activity {

	private android.location.Location currentLocation;
	private OTPProvider currentProvider;
	private MetadataSQLOpenHelper sqlOpenHelper;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		sqlOpenHelper = new MetadataSQLOpenHelper(getApplicationContext());

		// Build layout
		setContentView(R.layout.main);

		// Init location service
		LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
		
		Criteria criteria = new Criteria();
		String locationProvider = locationManager.getBestProvider( criteria, true );
		
		if (locationProvider==null)
		{
			createNoticeDialog(R.string.no_location);
			finish();
			return;
			
		}
		

		// Get current last known location
		currentLocation = locationManager.getLastKnownLocation(locationProvider);

		// Register the listener with the Location Manager to receive location updates
		locationManager.requestLocationUpdates(locationProvider, 10000, 0, locationListener);		
		
		// Build cities list
		AutoCompleteTextView cityField = (AutoCompleteTextView) findViewById(R.id.cityField);
		cityField.setAdapter(new ArrayAdapter<String>(getApplicationContext(), R.layout.city_item, getResources().getStringArray(R.array.cities)));


		((Button) findViewById(R.id.takeButton)).setOnClickListener(mTakeListener);
		((Button) findViewById(R.id.feedbackButton)).setOnClickListener(mFeedbackListener);
		
		
		// Need to present the user a selection
		AlertDialog.Builder builder = new AlertDialog.Builder(FindPlanActivity.this);
		final List<OTPProvider> providers = getOTPProviders();
		String providerTitles[] = new String[providers.size()];
		for(int i =0; i<providerTitles.length;++i)
			providerTitles[i] = providers.get(i).getTitle();
		builder.setTitle(R.string.select_provider);
		builder.setItems(providerTitles, new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				currentProvider = providers.get(which);
				((TextView) findViewById(R.id.provided_by_description)).setText(currentProvider.getDescription());
				dialog.dismiss();
			}
		});
		builder.setCancelable(true);
		builder.create().show();
		
	}


	
	private List<OTPProvider> getOTPProviders() {
		List<OTPProvider> providers = new ArrayList<OTPProvider>();
		try {
			Dao<OTPProvider, Integer> dao = sqlOpenHelper.getOTPProviderDao();
			CloseableIterator<OTPProvider> it = dao.iterator();
			while(it.hasNext())
			{
				OTPProvider provider = it.next();
				if (
						currentLocation == null
						||
						(
								currentLocation.getLatitude() < provider.getBounaryTop()
								&&
								currentLocation.getLatitude() > provider.getBounaryBottom()
								&&
								currentLocation.getLongitude() > provider.getBounaryLeft()
								&&
								currentLocation.getLongitude() < provider.getBounaryRight()
						)
						
				)
					providers.add(provider);
				
			}
			it.close();
		} catch (SQLException e) {
			Log.w(FindPlanActivity.class.toString(), "Cannot ",e);
		}
		
		return providers;
		
		
	}



	// Define a listener that responds to location updates
	private LocationListener locationListener = new LocationListener() {
		public void onLocationChanged(android.location.Location location) {
			currentLocation = location;
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {}

		public void onProviderEnabled(String provider) {}

		public void onProviderDisabled(String provider) {}
	};
	
	private void createNoticeDialog(int resource)
	{
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(resource);
		builder.setCancelable(true);
		builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.dismiss();
			}
		});
		builder.create().show();
		
	}


	private OnClickListener mFeedbackListener = new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			try
			{
				// Use reflectiont to see if kampyle's feedback form request is available(since the source code for it cannot be distributed at this time).
				// If we fail, we just display a user notice.
				Class feedbackFormRequestClass = Class.forName("com.kampyle.feedback.FeedbackFormRequest");
				Object feedbackFormRequest = feedbackFormRequestClass.getConstructor(new Class[] {String.class}).newInstance(getResources().getString(R.string.feedback_sitecode));
				feedbackFormRequest.getClass().getMethod("setFormId", new Class[] {int.class}).invoke(feedbackFormRequest,new Object[] {Integer.valueOf(getResources().getString(R.string.feedback_form_id))});
				feedbackFormRequest.getClass().getMethod("setLang", new Class[] {String.class}).invoke(feedbackFormRequest,new Object[] {Locale.getDefault().getLanguage()});
				Intent feedbackIntent = (Intent) feedbackFormRequestClass.getMethod("createIntent", new Class[] {Context.class} ).invoke(feedbackFormRequest,new Object[] {getApplicationContext()});
				if (feedbackIntent!=null)
					startActivity(feedbackIntent);
			}
			catch (Throwable e)
			{
				createNoticeDialog(R.string.no_feedback);
				
				
			}
		}
	};


	private OnClickListener mTakeListener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			if (currentLocation == null)
			{
				// No current location:-(
				createNoticeDialog(R.string.no_location);
			}
			else
			{
				AutoCompleteTextView cityField = (AutoCompleteTextView) findViewById(R.id.cityField);
				EditText addressField = (EditText) findViewById(R.id.addressField);
				
				String fullAddress =  addressField.getText() + "," + cityField.getText();
				new GeocodeTask().execute(fullAddress);
			}

		}
	};
	
	private class PlannerTask extends AsyncTask<PlanRequest, Void, PlanResult>
	{

		ProgressDialog progressDialog;
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			progressDialog = ProgressDialog.show(
														FindPlanActivity.this, 
														getResources().getString(R.string.progress_server_title,currentProvider.getHost()), 
														getResources().getString(R.string.progress_plan_message), 
														true, 
														false);
		}
		
		@Override
		protected PlanResult doInBackground(PlanRequest... params) {
			PlanRequest planRequest = params[0];
			Planner planner = new Planner(currentProvider.getHost(),currentProvider.getPlanUri(),currentProvider.getLocaleObject());
			try {
				return planner.generatePlan(planRequest);
			} catch (Exception e) {
				Log.e(FindPlanActivity.class.toString(), "Cannot get plan",e);
			}
			return null;
		}
		
		@Override
		protected void onPostExecute(final PlanResult result) {
			progressDialog.dismiss();
			if (result.getTripPlan().getItineraries().size()==0)
			{
				createNoticeDialog(R.string.plan_not_found);
				return;
			}
			Intent intent= new Intent(FindPlanActivity.this,DisplayPlanActivity.class);
			intent.putExtra("plan", result);
			startActivity(intent);
		}
		
	}
	
	private class GeocodeTask extends AsyncTask<String, Void, GeocoderResult>
	{
		ProgressDialog progressDialog;
		
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			progressDialog = ProgressDialog.show(
														FindPlanActivity.this, 
														getResources().getString(R.string.progress_server_title,currentProvider.getHost()), 
														getResources().getString(R.string.progress_geocode_message), 
														true, 
														false);
		}

		@Override
		protected GeocoderResult doInBackground(String... addresses) {
			// Yeah! use geodecoder
			android.location.Geocoder androidGeocoder = new android.location.Geocoder(getApplicationContext());
			try {
				List<Address> androidAddresses = androidGeocoder.getFromLocationName(addresses[0], 5);
				if (androidAddresses!=null && androidAddresses.size()>0)
				{
					List<Location> locations = new ArrayList<Location>();
					for(Address address : androidAddresses)
					{
						locations.add(new Location(address.getAddressLine(0), address.getLatitude(),address.getLongitude()));
					}
					return new GeocoderResult(locations);
				}
			} catch (IOException e1) {
				Log.e(FindPlanActivity.class.toString(), "Cannot get geocoding from android",e1);
			}
			Geocoder geocoder = new Geocoder(currentProvider.getHost(),currentProvider.getGeodecoderUri());
			try {
				return  geocoder.geodecode(addresses[0]);
			} catch (Exception e) {
				Log.e(FindPlanActivity.class.toString(), "Cannot get geocoding",e);
				return null;
			}
		}
		
		@Override
		protected void onPostExecute(final GeocoderResult geocoderResult) {
			progressDialog.dismiss();
			Location targetLocation = null;
			if (geocoderResult == null)
			{
				createNoticeDialog(R.string.cannot_geodeode);				
			}
			else if (geocoderResult.getLocations().size()==0)
			{
				createNoticeDialog(R.string.address_not_found);
			}
			else if (geocoderResult.getLocations().size()==1)
			{
				// Single match, no need to ask user
				buildAndDisplayPlan(geocoderResult.getLocations().get(0));
			}
			else
			{
				// Need to present the user a selection
				AlertDialog.Builder builder = new AlertDialog.Builder(FindPlanActivity.this);
				String addresses[] = new String[geocoderResult.getLocations().size()];
				for(int i =0; i<geocoderResult.getLocations().size();++i)
					addresses[i] = geocoderResult.getLocations().get(i).getAddress();
				builder.setTitle(R.string.select_exact_address);
				builder.setItems(addresses, new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						buildAndDisplayPlan(geocoderResult.getLocations().get(which));
						dialog.dismiss();
					}
				});
				builder.setCancelable(true);
				builder.create().show();
			}

		}
		
	}

	private void buildAndDisplayPlan(Location location) {
		PlanRequest planRequest = new PlanRequest();
		planRequest.setFrom(new Location(null, currentLocation.getLatitude(), currentLocation.getLongitude()));
//		planRequest.setFrom(new Location(null, 45.517847, -122.67692));
		planRequest.setTo(location);
		planRequest.setDate(new Date());
		new PlannerTask().execute(planRequest);
		
	}


}
